package luculent.μJavaActors.mytest.normal;

import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import luculent.μJavaActors.Actor;
import luculent.μJavaActors.DefaultActorManager;
import luculent.μJavaActors.DefaultMessage;
import luculent.μJavaActors.logging.DefaultLogger;
import luculent.μJavaActors.utils.Utils;

/** 
 * A set of runtime services for testing actors and a test case driver. 
 * 
 * @author BFEIGENB
 *
 */
public class DefaultActorTest2 extends Utils {

	public static final int MAX_IDLE_SECONDS = 10;

	// public static final int STEP_COUNT = 3 * 60;
	public static final int TEST_VALUE_COUNT = 1000;  
	
	DefaultActorManager actorManager;
	
	private Map<String, Actor> testActors = new ConcurrentHashMap<String, Actor>();

	static Random rand = new Random();
	
	protected int stepCount = 30;
	
	protected int threadCount = 10;
	
	public static final int COMMON_ACTOR_COUNT = 10;
	public static final int TEST_ACTOR_COUNT = 25;
	public static final int PRODUCER_ACTOR_COUNT = 25;

	protected List<ChangeListener> listeners = new LinkedList<ChangeListener>();
	
	protected static String[] types = new String[] { "widget", "framit", "frizzle", "gothca", "splat" };
	
	protected String title;
	
	volatile protected boolean done;
	

	public DefaultActorTest2() {
		super();
	} 

	public static int nextInt(int limit) {
		return rand.nextInt(limit);
	}

	protected DefaultActorManager getManager() {
		DefaultActorManager am = actorManager != null ? actorManager : new DefaultActorManager();
		return am;
	} 

	public static void dumpMessages(List<DefaultMessage> messages) {
		synchronized (messages) {
			if (messages.size() > 0) {
				for (DefaultMessage m : messages) {
					logger.info("%s", m);
				}
			}
		}
	} 

	public void addChangeListener(ChangeListener l) {
		if (!listeners.contains(l)) {
			listeners.add(l);
		}
	}

	public void removeChangeListener(ChangeListener l) {
		listeners.remove(l);
	}

	protected void fireChangeListeners(ChangeEvent e) {
		for (ChangeListener l : listeners) {
			l.stateChanged(e);
		}
	} 

	public static void main(String[] args) {
		DefaultActorTest2 at = new DefaultActorTest2();
		at.run(args);
		logger.trace("Done");
	}  

	public void run(String[] args) {
		done = false;
		DefaultLogger.getDefaultInstance().setIncludeDate(false);
		DefaultLogger.getDefaultInstance().setIncludeContext(false);
		DefaultLogger.getDefaultInstance().setIncludeCaller(false);
		DefaultLogger.getDefaultInstance().setIncludeThread(false);
		DefaultLogger.getDefaultInstance().setLogToFile(false);
		DefaultLogger.getDefaultInstance().setThreadFieldWidth(10);

		int sc = stepCount;
		int tc = threadCount;  
		
		DefaultActorManager am = getManager();
		try {
			//配置信息
			Map<String, Object> options = new HashMap<String, Object>();
			options.put(DefaultActorManager.ACTOR_THREAD_COUNT, tc);
			//在配置信息中启动线程进行信息监听和更新
			am.initialize(options);
			//创建actor
			for (int i = 0; i < COMMON_ACTOR_COUNT; i++) {
				Actor a = am.createActor(TestActor2.class, String.format("common%02d", i));
				if (a instanceof TestableActor2) {
					TestableActor2 ta = (TestableActor2) a;
					ta.setActorTest(this);
				}
				a.setCategory(TestActor2.class.getSimpleName());
				getTestActors().put(a.getName(), a);
				logger.trace("created: %s", a);
			}
			for (int i = 0; i < TEST_ACTOR_COUNT; i++) {
				Actor a = am.createActor(TestActor2.class, String.format("actor%02d", i));
				if (a instanceof TestableActor2) {
					TestableActor2 ta = (TestableActor2) a;
					ta.setActorTest(this);
				}
				getTestActors().put(a.getName(), a);
				logger.trace("created: %s", a);
			}
			//启动actor
			for (String key : getTestActors().keySet()) {
				am.startActor(getTestActors().get(key));
			} 
			
			for (int i = sc; i > 0; i--) {
				if (done) {
					break;
				}
				// see if idle a while
				long now = new Date().getTime();
				if (am.getActiveRunnableCount() == 0) {
					if (now - am.getLastDispatchTime() > MAX_IDLE_SECONDS * 1000
							&& now - am.getLastSendTime() > MAX_IDLE_SECONDS * 1000) {
						break;
					}
				}
				setStepCount(i);
				fireChangeListeners(new ChangeEvent(this));
				if (i < 10 || i % 10 == 0) {
					logger.trace("main waiting: %d...", i);
				}
				sleeper(1);
			}
			setStepCount(0);
			fireChangeListeners(new ChangeEvent(this));

			logger.trace("main terminating");
			
			sleeper(2);
			Actor a = am.createActor(TestActor2.class, String.format("actor%02d",1212));
			if (a instanceof TestableActor2) {
				TestableActor2 ta = (TestableActor2) a;
				ta.setActorTest(this);
			}
			am.startActor(a); 
			
			sleeper(10);
			am.terminateAndWait();
			
		
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void setStepCount(int stepCount) {
		this.stepCount = stepCount;
	}

	public int getStepCount() {
		return stepCount;
	}
	
	public void terminateRun() {
		done = true;
	}

	public int getThreadCount() {
		return threadCount;
	}

	public void setThreadCount(int threadCount) {
		this.threadCount = threadCount;
	}

	public void setTestActors(Map<String, Actor> testActors) {
		this.testActors = testActors;
	}

	public Map<String, Actor> getTestActors() {
		return testActors;
	} 

	public DefaultActorManager getActorManager() {
		return actorManager;
	}

	public void setActorManager(DefaultActorManager actorManager) {
		this.actorManager = actorManager;
	}
	/** Safely implement sleep(). */
	public static void sleep(long millis) {
		if (millis >= 0) {
			try {
				Thread.sleep(millis);
			} catch (InterruptedException e) {
				// e.printStackTrace(System.out);
			}
		}
	}
	public static void sleeper(int seconds) {
		int millis = seconds * 1000 + -50 + nextInt(100); // a little
															// variation
		// logger.trace("sleep: %dms", millis);
		sleep(millis);
	}
}
